package io.gitee.javalaoniu.mpgen.genarator;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * mysql 代码生成器
 * </p>
 *
 * @author javalaoniu
 */
public class MybatisPlusGenerator {

    /**
     * 运行main函数生成代码
     */
    public static void main(String[] args) {

        MybatisPlusGeneratorProperties properties = MybatisPlusGeneratorProperties.newBuilder()
                .jdbcUrl("jdbc:mysql://localhost:3306/test_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&tinyInt1isBit=false")
                .jdbcUser("root")
                .jdbcPwd("root!23")
                .codeSavePath("D:/Workspace/ideaIU-2020.3.4.win.Project/demo")
                .packageModuleName("demo")
                .packagePrefix("com.example")
                .tablePrefix("t_")
                .tableNames("t_demo")
                .requestMappingUrlPrefix("/test")
                .withSwagger(true)
                .build();

        MybatisPlusGenerator generator = new MybatisPlusGenerator(properties);
        generator.generate();
    }


    // ======================下面是生成器的代码

    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisPlusGenerator.class);

    private final MybatisPlusGeneratorProperties properties;


    public MybatisPlusGenerator(MybatisPlusGeneratorProperties properties) {
        this.properties = properties;
        // 检查属性是否完整
        checkProperties();

        // 打印属性
        printProperties();
    }

    private void generate() {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(properties.getCodeSavePath() + "/src/main/java");
        gc.setAuthor(properties.getAuthor());
        gc.setFileOverride(false);
        /*
            AUTO 数据库ID自增
            INPUT 用户输入ID
            ID_WORKER 全局唯一ID，Long类型的主键
            ID_WORKER_STR 字符串全局唯一ID
            UUID 全局唯一ID，UUID类型的主键
            NONE 该类型为未设置主键类型
         */
        gc.setIdType(IdType.AUTO);
        gc.setBaseResultMap(true);
        gc.setBaseColumnList(true);
        gc.setOpen(false);
        gc.setSwagger2(properties.isWithSwagger());
        gc.setActiveRecord(true);
        //新语言模式
        gc.setKotlin(false);
        mpg.setGlobalConfig(gc);

        //Service的命名
        gc.setServiceName("%sService");

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(properties.getJdbcUrl());
        if (properties.getSchema() != null) {
            dsc.setSchemaName(properties.getSchema());
        }
        dsc.setDriverName(properties.getDriverClass());
        dsc.setUsername(properties.getJdbcUser());
        dsc.setPassword(properties.getJdbcPwd());
        mpg.setDataSource(dsc);

        // 包配置参数
        PackageConfig pc = new PackageConfig();
        pc.setParent(properties.getPackagePrefix());
        pc.setModuleName(properties.getPackageModuleName());
        // 把数据库的表对应的实体放入到po包中，个人喜欢
        pc.setEntity(properties.getEntityPackageName() + ".po");
        pc.setController(properties.getControllerPackageName());
        pc.setMapper(properties.getMapperPackageName());
        pc.setService(properties.getServicePackageName());
        pc.setServiceImpl(properties.getServiceImplPackageName());
        mpg.setPackageInfo(pc);

        // 自定义配置参数，自定义参数也可以用于全局
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("module", properties.getPackageModuleName());
                map.put("jdbcUrl", properties.getJdbcUrl());
                map.put("jdbcUser", properties.getJdbcUser());
                map.put("jdbcPwd", properties.getJdbcPwd());
                map.put("requestMappingUrlPrefix", properties.getRequestMappingUrlPrefix());
                map.put("voEntityPackage", properties.getPackagePrefix()
                        + "." + properties.getPackageModuleName()
                        + "." + properties.getEntityPackageName()
                        + ".vo");
                map.put("commonPackge", properties.getPackagePrefix()
                        + "." + properties.getPackageModuleName()
                        + "." + properties.getEntityPackageName()
                        + ".common");
                map.put("utilPackge", properties.getPackagePrefix()
                        + "." + properties.getPackageModuleName()
                        + ".utils");
                map.put("configPackge", properties.getPackagePrefix()
                        + "." + properties.getPackageModuleName()
                        + ".config");
                this.setMap(map);
            }
        };

        // 设置自定义配置列表
        List<FileOutConfig> focList = customOutFiles();
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);


        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置自定义输出模板，如果设置为null并且focList中没有自定义输出则不会生成相应的文件。
        // 指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // xml使用下面自定义的
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(properties.isUseLombok());
        strategy.setRestControllerStyle(true);

        // 公共父类
        if (properties.getSuperEntityClass() != null) {
            strategy.setSuperEntityClass(properties.getSuperEntityClass());
        }
        if (properties.getSuperControllerClass() != null) {
            strategy.setSuperControllerClass(properties.getSuperControllerClass());
        }

        // 设置前缀
        strategy.setInclude(properties.getTableNames().split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(properties.getTablePrefix());
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

    private List<FileOutConfig> customOutFiles() {
        // 自定义输出配置列表
        List<FileOutConfig> focList = new ArrayList<>();

        /*
         默认mybatis-plus-generator的包下面有以下模板，可以根据自己需要重写
         controller.java.ftl
         service.java.ftl
         serviceImpl.java.ftl
         entity.java.ftl
         mapper.java.ftl
         mapper.xml.ftl
         其他模板引擎的：
         ...java.btl
         ...java.vm
         */

        // 添加自定义项
        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return properties.getCodeSavePath() + "/src/main/resources/" + properties.getMapperXmlPath()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        focList.add(new FileOutConfig("/templates/entityVO.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java/"
                        + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName().replace(".", "/")
                        + "/" + properties.getEntityPackageName()
                        + "/vo/" + tableInfo.getEntityName() + "VO" + StringPool.DOT_JAVA;
            }
        });

        focList.add(new FileOutConfig("/templates/AppResp.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/" + properties.getEntityPackageName()
                        + "/common/AppResp.java";
            }
        });
        focList.add(new FileOutConfig("/templates/PageParams.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/" + properties.getEntityPackageName()
                        + "/common/PageParams.java";
            }
        });
        focList.add(new FileOutConfig("/templates/PageResult.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/" + properties.getEntityPackageName()
                        + "/common/PageResult.java";
            }
        });
        focList.add(new FileOutConfig("/templates/OrikaBeanMapper.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/utils/OrikaBeanMapper.java";
            }
        });
        focList.add(new FileOutConfig("/templates/QueryWrapperHelper.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/utils/QueryWrapperHelper.java";
            }
        });
        focList.add(new FileOutConfig("/templates/BeanUtil.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/utils/BeanUtil.java";
            }
        });
        focList.add(new FileOutConfig("/templates/MybatisPlusConfig.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/config/MybatisPlusConfig.java";
            }
        });
        focList.add(new FileOutConfig("/templates/SwaggerConfig.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/java"
                        + "/" + properties.getPackagePrefix().replace(".", "/")
                        + "/" + properties.getPackageModuleName()
                        + "/config/SwaggerConfig.java";
            }
        });
        focList.add(new FileOutConfig("/templates/application.yml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输入文件名称 #mapper映射位置 不同服务位置不同
                return properties.getCodeSavePath() + "/src/main/resources/application-mp.yml";
            }
        });
        return focList;
    }

    private void checkProperties() {
        if (properties == null) {
            LOGGER.error("properties is not setting");
            throw new RuntimeException("properties is not setting");
        }

        if (properties.getTableNames() == null) {
            LOGGER.error("properties tableName is not setting");
            throw new RuntimeException("properties tableName is not setting");
        }
    }

    private void printProperties() {
        System.out.println(properties.toString());
    }

}
